package com.projectframe.base.db.dao;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.transform.ResultTransformer;

@SuppressWarnings("serial")
public class BaseDaoColumnToBean implements ResultTransformer
{
    private static final Logger LOG = Logger.getLogger(BaseDaoColumnToBean.class);
    private final Class<?>      resultClass;
    
    public BaseDaoColumnToBean(Class<?> resultClass)
    {
        if (null == resultClass)
            throw new IllegalArgumentException("resultClass cannot be null");
        
        this.resultClass = resultClass;
    }

    @SuppressWarnings("unchecked")
    public List<?> transformList(List collection)
    {
        return collection;
    }

    // 結果轉換時, HIBERNATE調用此方法
    public Object transformTuple(Object[] tuple, String[] aliases)
    {
        Object result = null;

        try
        {
            result = resultClass.newInstance();
            final int columnSize = aliases.length;

            for (int i = 0; i < columnSize; i++)
            {
                String alias = aliases[i];
                setProperty(result, alias, tuple[i]);
            }
        }
        catch (InstantiationException e)
        {
            e.printStackTrace();
            throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName());
        }
        catch (IllegalAccessException e)
        {
            e.printStackTrace();
            throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        
        return result;
    }

    private static boolean setProperty(final Object bean, final String propertyName, final Object value)
    {
        boolean result = false;
        
        try
        {
            PropertyUtils.setProperty(bean, propertyName, value);
            result = true;
        }
        catch (NoSuchMethodException e)
        {
            for (Field tmpField : bean.getClass().getDeclaredFields())
            {
                if (!tmpField.getDeclaringClass().isPrimitive() && tmpField.getType().getName().startsWith("com.pic.pos3g."))
                {
                    tmpField.setAccessible(true);
                    Object tmpObj;
                    try
                    {
                        tmpObj = tmpField.get(bean);
                        if (null == tmpObj)
                        {
                            tmpObj = tmpField.getType().newInstance();
                            setProperty(bean, tmpField.getName(), tmpObj);
                        }
                        boolean tmpReturn = setProperty(tmpObj, propertyName, value);
                        if (tmpReturn)
                        {
                            result = tmpReturn;
                            break;
                        }
                    }
                    catch (IllegalArgumentException e1)
                    {
                        LOG.error("Could not instantiant resultclass: " + e1.getMessage(), e1);
                        throw new HibernateException("Could not instantiate resultclass: "
                                + bean.getClass().getName());
                    }
                    catch (IllegalAccessException e1)
                    {
                        LOG.error("Could not instantiant resultclass: " + e1.getMessage(), e1);
                        throw new HibernateException("Could not instantiate resultclass: "
                                + bean.getClass().getName());
                    }
                    catch (InstantiationException e1)
                    {
                        LOG.error("Could not instantiant resultclass: " + e1.getMessage(), e1);
                        throw new HibernateException("Could not instantiate resultclass: "
                                + tmpField.getType().getName());
                    }
                }
                else
                {
                    final String tmpPropertyName = propertyName.replaceAll("_", "");

                    if (StringUtils.equalsIgnoreCase(tmpPropertyName, tmpField.getName()))
                    {
                        boolean tmpReturn = setProperty(bean, tmpField.getName(), value);
                        if (tmpReturn)
                        {
                            result = tmpReturn;
                            break;
                        }
                    }
                }
            }
        }
        catch (IllegalAccessException e)
        {
            LOG.error("Could not instantiant resultclass: " + e.getMessage(), e);
            throw new HibernateException("Could not instantiate resultclass: "
                    + bean.getClass().getName());
        }
        catch (InvocationTargetException e)
        {
            LOG.error("Could not instantiant resultclass: " + e.getMessage(), e);
            throw new HibernateException("Could not instantiate resultclass: "
                    + bean.getClass().getName());
        }
        catch (IllegalArgumentException e)
        {
            try
            {
                final Field field = bean.getClass().getDeclaredField(propertyName);
                final String fieldType = field.getType().getName();

                if ((value instanceof BigInteger))
                {
                    if (StringUtils.equals("long", fieldType))
                    {
                        final long data = ((BigInteger) value).longValue();
                        PropertyUtils.setProperty(bean, propertyName, data);
                    }
                    else if (StringUtils.equals("java.lang.Integer", fieldType))
                    {
                        final Integer data = Integer.valueOf(((BigInteger) value).intValue());
                        PropertyUtils.setProperty(bean, propertyName, data);
                    }
                    else if (StringUtils.equals("int", fieldType))
                    {
                        final int data = ((BigInteger) value).intValue();
                        PropertyUtils.setProperty(bean, propertyName, data);
                    }
                    else if (StringUtils.equals("java.lang.Long", fieldType))
                    {
                        final Long data = Long.valueOf(((BigInteger) value).longValue());
                        PropertyUtils.setProperty(bean, propertyName, data);
                    }
                }
                else if ((value instanceof BigDecimal))
                {
                    if (StringUtils.equals("java.lang.Integer", fieldType))
                    {
                        final Integer data = Integer.valueOf(((BigDecimal) value).intValue());
                        PropertyUtils.setProperty(bean, propertyName, data);
                    }
                    else if (StringUtils.equals("int", fieldType))
                    {
                        final int data = ((BigDecimal) value).intValue();
                        PropertyUtils.setProperty(bean, propertyName, data);
                    }
                    else if (StringUtils.equals("long", fieldType))
                    {
                        final long data = ((BigDecimal) value).longValue();
                        PropertyUtils.setProperty(bean, propertyName, data);
                    }
                    else if (StringUtils.equals("java.lang.Long", fieldType))
                    {
                        final Long data = Long.valueOf(((BigDecimal) value).longValue());
                        PropertyUtils.setProperty(bean, propertyName, data);
                    }
                }
                else
                {
                    LOG.error("setProperty error", e);
                    throw new HibernateException("Could not instantiate resultclass: "
                            + bean.getClass().getName());
                }
            }
            catch (SecurityException e2)
            {
                LOG.error("get declaredField error, " + e2.getMessage(), e2);
                throw new HibernateException("Could not instantiate resultclass: "
                        + bean.getClass().getName());
            }
            catch (NoSuchFieldException e2)
            {
                LOG.error("get declaredField error, " + e2.getMessage(), e2);
                throw new HibernateException("Could not instantiate resultclass: "
                        + bean.getClass().getName());
            }
            catch (IllegalAccessException e2)
            {
                LOG.error("Could not instantiant resultclass: " + e2.getMessage(), e2);
                throw new HibernateException("Could not instantiate resultclass: "
                        + bean.getClass().getName());
            }
            catch (InvocationTargetException e2)
            {
                LOG.error("Could not instantiant resultclass: " + e2.getMessage(), e2);
                throw new HibernateException("Could not instantiate resultclass: "
                        + bean.getClass().getName());
            }
            catch (NoSuchMethodException e2)
            {
                LOG.error("Could not instantiant resultclass: " + e2.getMessage(), e2);
                throw new HibernateException("Could not instantiate resultclass: "
                        + bean.getClass().getName());
            }
        }
        
        return result;
    }
}
